package com.gkzf.ai.module.infra.framework.file.core.client.s3;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.gkzf.ai.module.infra.framework.file.core.client.AbstractFileClient;
import com.volcengine.tos.TOSV2;
import com.volcengine.tos.TOSV2ClientBuilder;
import com.volcengine.tos.TosClientException;
import com.volcengine.tos.TosServerException;
import com.volcengine.tos.model.object.PutObjectFromFileInput;
import com.volcengine.tos.model.object.PutObjectFromFileOutput;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

@Slf4j
public class PutObjectFromFileExample extends AbstractFileClient<S3FileClientConfig> {

    private AmazonS3Client client;


    public PutObjectFromFileExample(Long id, S3FileClientConfig config) {
        super(id, config);
    }


    /**
     * 将字节数组写入一个临时文件，并返回一个 FileInputStream 读取该临时文件。
     * WARNING: 此方法会在文件系统上创建一个临时文件，调用者有责任在使用完毕后删除该临时文件。
     * 考虑在返回的 File 对象上调用 deleteOnExit() (有限制)，或者在 finally 块中手动删除。
     *
     * @param content 要写入文件的字节内容
     * @param tempFilePrefix 临时文件名前缀 (可以为 null 或 "")
     * @param tempFileSuffix 临时文件名后缀 (可以为 null 或 "")
     * @return 读取临时文件的 FileInputStream，或者抛出 IOException
     * @throws IOException 如果发生文件创建或写入错误
     */
    public static FileInputStream bytesToFileInputStream(
            byte[] content,
            String tempFilePrefix,
            String tempFileSuffix) throws IOException {

        if (content == null) {
            throw new IllegalArgumentException("Input byte array cannot be null");
        }

        // 1. 创建一个临时文件
        // Files.createTempFile 会在系统的默认临时目录中创建一个具有唯一名称的文件
        Path tempFilePath = null;
        try {
            tempFilePath = Files.createTempFile(tempFilePrefix, tempFileSuffix);
            File tempFile = tempFilePath.toFile();

            // 2. 将字节数组的内容写入临时文件
            // StandardOpenOption.CREATE: 如果文件不存在则创建
            // StandardOpenOption.WRITE: 打开文件进行写操作，如果文件有内容会截断清空
            Files.write(tempFilePath, content, StandardOpenOption.CREATE, StandardOpenOption.WRITE);

            // 3. 创建一个 FileInputStream 来读取这个临时文件
            FileInputStream fis = new FileInputStream(tempFile);

            // 重要的注意事项：临时文件 tempFile 仍然存在于文件系统中。
            // 你需要在不再需要 FileInputStream 后删除这个文件。
            // 一种简单的方法是调用 tempFile.deleteOnExit(); 但它并不总是可靠，
            // 特别是在应用程序异常退出或长时间运行的情况下。
            // 更可靠的方式是调用者在使用完 FileInputStream 后，
            // 在适当的时候（例如，finally 块或资源管理类中）通过返回的 File 对象手动删除文件。

            tempFile.deleteOnExit(); // 简单但不可靠的清理方式，更推荐手动删除

            return fis;

        } catch (IOException e) {
            // 如果在文件创建或写入过程中发生错误，需要清理可能已部分创建的文件
            if (tempFilePath != null && Files.exists(tempFilePath)) {
                try {
                    Files.delete(tempFilePath);
                } catch (IOException cleanupException) {
                    // 记录清理失败的日志，但不影响原始异常
                    System.err.println("Error cleaning up temporary file after failed write: " + tempFilePath + " - " + cleanupException.getMessage());
                }
            }
            throw e; // 重新抛出原始的写入错误
        }
    }


    @Override
    public String upload(byte[] content, String path, String type) throws Exception {
        // 元数据，主要用于设置文件类型
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(type);
        objectMetadata.setContentLength(content.length); // 如果不设置，会有 “ No content length specified for stream data” 警告日志
        TOSV2 tos = new TOSV2ClientBuilder().build("cn-beijing", config.getEndpoint(), config.getAccessKey(), config.getAccessSecret());

        try {
            PutObjectFromFileInput putObjectInput = new PutObjectFromFileInput()
                    .setBucket(config.getBucket()).setKey(type).setFileInputStream(bytesToFileInputStream(content,"crm.",type));
            PutObjectFromFileOutput output = tos.putObjectFromFile(putObjectInput);

            log.info("putObject succeed, object's etag is " + output.getEtag());
            log.info("putObject succeed, object's crc64 is " + output.getHashCrc64ecma());
        } catch (TosClientException e) {
            // 操作失败，捕获客户端异常，一般情况是请求参数错误，此时请求并未发送
            log.info("putObject failed");
            log.info("Message: " + e.getMessage());
            if (e.getCause() != null) {
                e.getCause().printStackTrace();
            }
        } catch (TosServerException e) {
            // 操作失败，捕获服务端异常，可以获取到从服务端返回的详细错误信息
            log.info("putObject failed");
            log.info("StatusCode: " + e.getStatusCode());
            log.info("Code: " + e.getCode());
            log.info("Message: " + e.getMessage());
            log.info("RequestID: " + e.getRequestID());
        } catch (Throwable t) {
            // 作为兜底捕获其他异常，一般不会执行到这里
            log.info("putObject failed");
            log.info("unexpected exception, message: " + t.getMessage());
        }
        // 拼接返回路径
        return config.getDomain() + "/" + path;
    }

    @Override
    public void delete(String path) throws Exception {

    }

    @Override
    public byte[] getContent(String path) throws Exception {
        return new byte[0];
    }


    @Override
    protected void doInit() {

    }
}